#pragma once
#include "Reactor.h"
#include "Util.h"
#include<errno.h>
#include<fcntl.h>

bool SetNonBlock(int sock)
{
    int fl = fcntl(sock,F_GETFL);
    if(fl < 0)
    {
        std::cout << "fcntl error" << std::endl;
        return false;
    }
    fcntl(sock,F_SETFL,fl | O_NONBLOCK);
    return true;
}
int recver_helper(int sock,std::string& in)
{
    while(true)
    {
        char buffer[128];
        int size = recv(sock,buffer,sizeof(buffer)-1,0);
        if(size < 0)
        {
            if(errno == EAGAIN || errno == EWOULDBLOCK)
                return 0;
            else if(errno == EINTR)
                continue;
            else
                return -1;
        }
        else if(size == 0)
            return -1;
        buffer[size] = '\0';
        in += buffer;
    }
}
void recver(EventItem& item)
{
    if(recver_helper(item.sock_,item.inbuffer) == -1)
    {
        if(item.error_handler)
            item.error_handler(item);
        return;
    }
    std::vector<std::string> packages;
    Util::StringSplit(item.inbuffer,packages,"X");
    for(auto& package : packages)
    {
        int x = 0, y = 0;
        char op;
        if(!Util::ParsePackage(package,x,y,op))
            continue;
        int result = 0;
        switch(op)
        {
            case '+':
                result = x+y;
                break;
            case '-':
                result = x-y;
                break;
            case '*':
                result = x*y;
                break;
            case '/':
                result = x/y;
                break;
        }
        std::string response = package;
        response += "=";
        response += std::to_string(result);
        response += "X";

        item.outbuffer += response;
        (item.R_)->EnableReadWrite(item.sock_,true,true);
    }
}
int sendhelper(int sock,std::string& out)
{
    int total = 0;
    out += '\n';
    const char* start = out.c_str();
    int size = out.size();
    while(true)
    {
        int s = send(sock,start+total,size-total,0);
        if(s > 0)
        {
            total += s;
            if(total == size)
            {
                out.clear();
                return 1;
            }
        }
        else
        {
            if(errno == EINTR)
                continue;
            else if(errno == EAGAIN || errno == EWOULDBLOCK)
            {
                out.erase(0,total);
                return 0;
            }
            else
                return -1;
        }
    }
}
void sender(EventItem& item)
{
    int ret = sendhelper(item.sock_,item.outbuffer);
    if(ret == -1)
    {
        if(item.error_handler)
            item.error_handler(item);
    }
    else if(ret == 1)
        (item.R_)->EnableReadWrite(item.sock_,true,false);
    else if(ret == 0)
        (item.R_)->EnableReadWrite(item.sock_,true,true);
}
void errorer(EventItem& item)
{
    (item.R_)->DelEvent(item.sock_);
}
void Accepter(EventItem& item)
{
    while(true)
    {
        struct sockaddr_in peer;
        socklen_t len = sizeof(peer);
        int sock = accept(item.sock_,(struct sockaddr*)&peer,&len);
        if(sock < 0)
        {
            if(errno == EAGAIN || errno == EWOULDBLOCK)
                return ;
            else if(errno == EINTR)
                continue;
            else
            {
                std::cerr << "accept error" << std::endl;
                return;
            }
        }
        SetNonBlock(sock);
        EventItem sock_item;
        sock_item.sock_ = sock;
        sock_item.R_ = item.R_;
        sock_item.RegisterCallBacks(recver,sender,errorer);

        Reactor* R = item.R_;
        R->AddEvent(sock,EPOLLIN | EPOLLET, sock_item);
    }
}